package nl.siegmann.epublib.domain;

import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import nl.siegmann.epublib.service.MediatypeService;
import nl.siegmann.epublib.util.IOUtil;

//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;

/**
 * A Resource that loads its data only on-demand.
 * This way larger books can fit into memory and can be opened faster.
 */
public class LazyResource extends Resource {

    private String TAG = getClass().getName();

    /**
     *
     */
    private static final long serialVersionUID = 5089400472352002866L;
    private String filename;
    private long cachedSize;

//	private static final Logger LOG = LoggerFactory.getLogger(LazyResource.class);

    /**
     * Creates a Lazy resource, by not actually loading the data for this entry.
     * <p>
     * The data will be loaded on the first call to getData()
     *
     * @param filename the file name for the epub we're created from.
     * @param size     the size of this resource.
     * @param href     The resource's href within the epub.
     */
    public LazyResource(String filename, long size, String href) {
        super(null, null, href, MediatypeService.determineMediaType(href));
        this.filename = filename;
        this.cachedSize = size;
    }

    /**
     * Creates a Resource that tries to load the data, but falls back to lazy loading.
     * <p>
     * If the size of the resource is known ahead of time we can use that to allocate
     * a matching byte[]. If this succeeds we can safely load the data.
     * <p>
     * If it fails we leave the data null for now and it will be lazy-loaded when
     * it is accessed.
     *
     * @param in
     * @param filename
     * @param length
     * @param href
     * @throws IOException
     */
    public LazyResource(InputStream in, String filename, int length, String href) throws IOException {
        super(null, IOUtil.toByteArray(in, length), href, MediatypeService.determineMediaType(href));
        this.filename = filename;
        this.cachedSize = length;
    }

    /**
     * Gets the contents of the Resource as an InputStream.
     *
     * @return The contents of the Resource.
     * @throws IOException
     */
    @Override
    public InputStream getInputStream() throws IOException {
        if (isInitialized()) {
            return new ByteArrayInputStream(getData());
        } else {
            return getResourceStream();
        }
    }

    /**
     * Initializes the resource by loading its data into memory.
     *
     * @throws IOException
     */
    public void initialize() throws IOException {
        getData();
    }

    /**
     * The contents of the resource as a byte[]
     * <p>
     * If this resource was lazy-loaded and the data was not yet loaded,
     * it will be loaded into memory at this point.
     * This included opening the zip file, so expect a first load to be slow.
     *
     * @return The contents of the resource
     */
    @Override
    public byte[] getData() {
        try {
            if (data == null) {
                Log.d(TAG, "Initializing lazy resource " + filename + "#" + this.getHref());
                InputStream in = getResourceStream();
                byte[] readData = IOUtil.toByteArray(in, (int) this.cachedSize);
                if (readData == null) {
                    throw new IOException("Could not load the contents of entry " + this.getHref() + " from epub file " + filename);
                } else {
                    this.data = readData;
                }

                in.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(getClass().getSimpleName(),e.getMessage());
        }
        return data;
    }


    private InputStream getResourceStream() throws IOException {
        ZipFile zipFile = new ZipFile(filename);
        ZipEntry zipEntry = zipFile.getEntry(originalHref);
        if (zipEntry == null) {
            zipFile.close();
            throw new IllegalStateException("Cannot find entry " + originalHref + " in epub file " + filename);
        }
        return new ResourceInputStream(zipFile.getInputStream(zipEntry), zipFile);
    }

    /**
     * Tells this resource to release its cached data.
     * <p>
     * If this resource was not lazy-loaded, this is a no-op.
     */
    @Override
    public void close() {
        if (this.filename != null) {
            this.data = null;
        }
    }

    /**
     * Returns if the data for this resource has been loaded into memory.
     *
     * @return true if data was loaded.
     */
    public boolean isInitialized() {
        return data != null;
    }

    /**
     * Returns the size of this resource in bytes.
     *
     * @return the size.
     */
    @Override
    public long getSize() {
        if (data != null) {
            return data.length;
        }

        return cachedSize;
    }
}
